package com.exception;


import java.io.IOException;
import java.util.List;

import javax.naming.NoPermissionException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.ModelAndView;
import com.utils.StringUtils;

@ControllerAdvice
public class GlobalExceptionHandler {
	private final static Logger loger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
	@ExceptionHandler(LoginException.class)
    @ResponseBody
	public ModelAndView resolveLoginException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
		//没有登录时，得定向登录Url，进行上下文重建
		String requrl =  request.getServletPath()+(StringUtils.isBlank(request.getQueryString())?"":"?"+request.getQueryString());
		if(loger.isInfoEnabled()){
			this.loger.info("未登录，准备重建上下文，请求URL="+requrl);
		}
		String xType = request.getParameter("x_type");
		String viewName = "forward:/login?requrl="+requrl;
		ModelAndView mv = new ModelAndView(viewName);
        if(StringUtils.isNotBlank(xType)){
            mv.addObject("x_type", xType);
        }
		response.reset();
		return mv;
	}
    @ExceptionHandler(Exception.class)
    @ResponseBody
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

		// 将时间记入日志，辅助从日志文件中查找到对应错误
		Long time = System.currentTimeMillis();
		String message = ex.getMessage();
		if (ex instanceof I18nMessage) {
		}
		// 业务异常只显示下消息
		if (!(ex instanceof BusinessException) && !(ex instanceof BindException)) {
			if (!ex.getClass().getName().contains("ClientAbortException")) {// 屏蔽掉客户端提前关闭页面导致的异常
				loger.error("[{}][{}][{}]:{} ,request parameters:{}", time, request
						.getRequestURI(), message,  ex);
			}
		} else {
			loger.warn("[{}][{}][{}]:{} ,request parameters:{}",  time, request
					.getRequestURI(), message);
		}
		// 如果是ajax请求响应头会有，x-requested-with；
		boolean isAjax = "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
		if (!isAjax) {
            isAjax = "1".equals(request.getHeader("x-ajax"));
		}
        if (!isAjax) {
            isAjax = "1".equals(request.getParameter("x-ajax-file"));
        }
        String xType = request.getParameter("x_type");
		String viewName = "500";



		if (ex instanceof LoginException) {
			// 登录失败
			if (!isAjax) {
				//没有登录时，得定向登录Url，进行上下文重建
				String requrl =  request.getServletPath()+(StringUtils.isBlank(request.getQueryString())?"":"?"+request.getQueryString());
				if(loger.isInfoEnabled()){
					this.loger.info("未登录，准备重建上下文，请求URL="+requrl);
				}
				viewName = "forward:/login?requrl="+requrl;	
			}
		} else if (ex instanceof NoPermissionException) {
			// 没有权限
			if (!isAjax) {
				viewName = "forbidden";
			}
		} else if (ex instanceof BusinessException) {
			// 普通错误,前端显示错误信息
		} else if (ex instanceof ServletException) {
			// Servlet错误，一般为请求非法
			message = "非法请求或参数有误";
		}else if (ex instanceof MaxUploadSizeExceededException) {
            // 文件太大
            message = "文件大小超过允许范围";
        } else if (ex instanceof BindException) {
			if (message.contains("BeanPropertyBindingResult")) {
				message = "类型转换错误";
			} else {
				// 参数绑定错误
				message = "非法请求或参数有误";
				BindException bdex = (BindException) ex;
				List<ObjectError> errors = bdex.getAllErrors();
				if (errors != null && errors.size() > 0) {
					int size = errors.size();
					int i = 1;
					StringBuilder builder = new StringBuilder();
					builder.append(String.format("校验不通过：共%d项错误</br>", size));
					for (ObjectError error : errors) {
						if (i <= 6) {
							builder.append(error.getDefaultMessage()).append("</br>");
						} else {
							builder.append("......");
							break;
						}
						i++;
					}
					message = builder.toString();
				}
			}
		} else {
			// 其他错误信息非友好，前端只显示有错误，但不具体提示
			message = "服务端异常";
		}

		if (isAjax) {
			StringBuilder sb = new StringBuilder();
			String callback = request.getParameter("callback");
			boolean isJsonp = StringUtils.isNotBlank(callback);
			if (isJsonp) {
				sb.append(callback).append("(");
			}
			sb.append("{\"_eid\":").append(time).append(",\"status\":").append(",\"message\":\"")
					.append(message).append("\"}");
			if (isJsonp) {
				sb.append(");");
			}
			response.reset();
			try {
				response.setContentType("text/html;charset=UTF-8");
				response.getWriter().print(sb.toString());
				return  new ModelAndView();
			} catch (IOException ignored) {
			}
		}
		ModelAndView mv = new ModelAndView(viewName);
        if(StringUtils.isNotBlank(xType)){
            mv.addObject("x_type", xType);
        }
		response.reset();
		return mv;

	}
}
